package pt.iscte.dcti.pa.model;

import java.util.List;
import java.util.Observable;
import java.util.Observer;

import utils.Common;

public class Cell extends Observable implements Observer {

	private int abscissa; // XX
	private int ordinate; // YY
	private String value = null;
	private WorksheetModel model;

	public Cell(int abscissa, int ordinate, WorksheetModel _model) {
		this.abscissa = abscissa;
		this.ordinate = ordinate;
		this.model = _model;
	}

	public int getAbscissa() {
		return abscissa;
	}

	public int getOrdinate() {
		return ordinate;
	}

	public String getValue() {

		return this.value;
	}

	public void setValue(String value) {

		this.value = value;
	}

	public void setObserver(Observer obs) {
		this.addObserver(obs);
	}

	@Override
	public void update(Observable o, Object arg) {

		Cell observableCell = (Cell) o;
		System.out.println("Handling cell: " + this.toString());
		String updatedValue = this.doMagik();
	}

	public String doMagik() {
		String newValue = this.value;

		if (newValue.charAt(0) == '=') {

			newValue = newValue.substring(1); // retira o "="

			List<String> operands = Common.getMatches("[A-Z]+\\d+", newValue);

			int[] coordinates;
			String cellResult = null;
			for (String operand : operands) {
				coordinates = Common.GetCoordinates(operand);

				try {

					cellResult = computeCellValue(coordinates);

				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				newValue = newValue.replace(operand, cellResult);
			}

		}
		newValue = doMath(newValue);

		setChanged();
		notifyObservers(newValue);

		return newValue;
	}

	private String computeCellValue() {
		Cell targetCell = model.getCell(abscissa, ordinate);
		targetCell.addObserver(this);
		String cellValue = targetCell.getValue();

		// se for um numero
		if (Common.isDigit(cellValue)) {
			return cellValue;
		}

		List<String> operands = Common.getMatches("[A-Z]+\\d+", cellValue);
		String auxCellValue;
		for (String operand : operands) {

			try {

				auxCellValue = computeCellValue(Common.GetCoordinates(operand));
				cellValue = cellValue.replace(operand, auxCellValue);

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

		return doMath(cellValue);
	}

	private String computeCellValue(int[] coordinates) throws Exception {
		Cell targetCell = model.getCell(coordinates[0], coordinates[1]);
		targetCell.addObserver(this);
		String cellValue = targetCell.getValue();

		// se for um numero
		if (Common.isDigit(cellValue)) {
			return cellValue;
		}

		List<String> operands = Common.getMatches("[A-Z]+\\d+", cellValue);
		String auxCellValue;
		for (String operand : operands) {
			auxCellValue = computeCellValue(Common.GetCoordinates(operand));
			cellValue = cellValue.replace(operand, auxCellValue);
		}

		return doMath(cellValue);
	}

	private String doMath(String expression) {

		int res = 0;

		List<String> matches;

		matches = Common.getMatches("\\d+([\\*/]\\d+)+", expression);

		for (String string : matches) {
			String result = getPrecedentResult(string);
			if (result == null) {
				return null;
			}
			expression = expression.replace(string, result);
		}

		//matches = Common.getMatches("\\-?\\d+([\\+\\-]\\d+)+", expression);
		matches = Common.getMatches("(\\-|\\+)?\\d+", expression);		//Ja nao ha produtos ou divisoes

		for (String part : matches) {
			res += Integer.parseInt(part);
		}

		return Integer.toString(res);
	}

	private String getPrecedentResult(String string) {

		List<String> matches = Common.getMatches("\\d|[\\*/]", string);
		boolean doProduct = false, doDivision = false;
		int aux = 0;
		
		for (String macth : matches) {
			switch (macth) {
			case "*":
				doProduct = true;
				break;
			case "/":
				doDivision = true;
				break;
			default:
				if (doProduct) {
					aux *= Integer.parseInt(macth); 
					doProduct = false;
				}
				else if (doDivision) {
					if (Integer.parseInt(macth) == 0) {
						return null;
					}
					aux /= Integer.parseInt(macth);
					doDivision = false;
				}
				else {
					aux = Integer.parseInt(macth);
				}
				break;			
			}
		}
		return Integer.toString(aux);
	}
	
	private String getSubsequentResult(String expression)
	{
		List<String> matches = Common.getMatches("\\d|[\\+\\-]", expression);
		boolean doSum = false, doSubtraction = false;
		int aux = 0;
		
		for (String macth : matches) {
			switch (macth) {
			case "+":
				doSum = true;
				break;
			case "-":
				doSubtraction = true;
				break;
			default:
				if (doSum) {
					aux += Integer.parseInt(macth);
					doSum = false;
				}
				else if (doSubtraction) {
					aux -= Integer.parseInt(macth);
					doSubtraction = false;
				}
				else {
					aux = Integer.parseInt(macth);
				}
				break;			
			}
		}
		return Integer.toString(aux);
		
	}
	
	@Override
	public String toString() {
		return String.format("Abscissa: %s; Ordinate: %s; Value: %s",
				this.getAbscissa(), this.getOrdinate(), this.getValue());
	}
}
